/*
 * Copyright (c) 2006-2007 Zsolt Szasz <zsolt at lorecraft dot com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package org.lorecraft.routes;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

public class DynamicElement extends AbstractElement implements Element {

	private final String[] sections;
	private boolean singleVariable = true;

	public DynamicElement(String element) {
		super(element);
		this.sections = split(element);
	}

	private String[] split(String element) {
		StringTokenizer tokenizer = new StringTokenizer(element, ".-|_", true);
		String[] sections = new String[tokenizer.countTokens()];
		int i = 0;
		while (tokenizer.hasMoreTokens()) {
			sections[i++] = tokenizer.nextToken();
		}
		if (sections.length > 1) {
			singleVariable = false;
		}
		return sections;
	}

	public void map(ProcessingContext context) {
		if (!singleVariable && !context.hasMoreElements()) {
			context.getRouteMapping().markInvalid();
			return;
		}
		if (context.hasMoreElements()) {
			String currentElementString = context.getCurrentElementString();
			String[] currentSections;
			if (singleVariable) {
				currentSections = new String[] {currentElementString};
			} else {
				currentSections = split(currentElementString);
			}
			if (currentSections.length != sections.length) {
				context.getRouteMapping().markInvalid();
			} else {
				Map<String, String> vars = new HashMap<String, String>();
				for (int i = 0; i < currentSections.length; i++) {
					if (sections[i].startsWith(":")) {
						if (checkValue(currentSections[i])) {
							vars.put(sections[i].substring(1), currentSections[i]);
						} else {
							context.getRouteMapping().markInvalid();
							break;
						}
					} else if (!sections[i].equals(currentSections[i])) {
						context.getRouteMapping().markInvalid();
						break;
					}
				}
				if (context.getRouteMapping().isValid()) {
					for (Iterator<Map.Entry<String, String>> i = vars.entrySet().iterator(); i.hasNext();) {
						Map.Entry<String, String> entry = i.next();
						context.getRouteMapping().setParameter(entry.getKey(), entry.getValue());
					}
					context.advance();
					next.map(context);
				}
			}
		} else {
			next.map(context);
		}
	}

	protected boolean checkValue(String value) {
		// no special rules
		return true;
	}

	public void getPath(StringBuilder builder, Map<String, String> params, List<String> undefineds) {
		StringBuilder buf = new StringBuilder();
		int missedVariableSubstitutions = 0;
		for (int i = 0; i < sections.length; i++) {
			String currentSection = sections[i];
			if (currentSection.charAt(0) == ':') {
				String key = currentSection.substring(1);
				String value = params.get(key);
				if (value !=null ) {
					buf.append(value);
					params.remove(key);
				} else {
					missedVariableSubstitutions++;
				}
			} else {
				buf.append(sections[i]);
			}
		}
		if (missedVariableSubstitutions > 0) {
			if (singleVariable) {
				undefineds.add(element.substring(1));
			} else {
				throw new IllegalArgumentException("Partially satisfied mixed dynamic element encountered");
			}
		} else {
			builder.append('/');
			builder.append(buf);
		}


		next.getPath(builder, params, undefineds);
	}


}
